package com.jiuqi.abc.model.service;

import com.jiuqi.abc.intf.ABCIntfException;
import com.jiuqi.abc.intf.provider.dimension.DimBean;
import com.jiuqi.abc.intf.provider.dimension.DimFolder;
import com.jiuqi.abc.intf.provider.dimension.DimObj;
import com.jiuqi.abc.intf.provider.dimension.IDimProvider;
import com.jiuqi.abc.intf.provider.member.IMemberProvider;
import com.jiuqi.abc.intf.provider.member.Member;
import com.jiuqi.abc.model.vo.*;
import com.jiuqi.bi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 各成本维度（成本池、分摊维度、成本对象）维成员service
 */
@Service
public class ABCDimensionService {

    @Autowired
    private IDimProvider dimProvider;
    @Autowired
    private IMemberProvider memberProvider;

    @Autowired
    private ABCModelService abcModelService;

    Logger logger = LoggerFactory.getLogger(ABCDimensionService.class);

    /**
     * 根据 父节点维度代码 获取下级节点
     * @param parentName 父节点维度代码
     * @return 下级维度节点
     */
    public List<DimensionVo> getDimensionChildrens(String parentName) {
        List<DimensionVo> dimensionVoList = new ArrayList<>();
        try {
            List<DimObj> childrenList = dimProvider.getChildren(parentName);
            for (DimObj childrenItem : childrenList) {
                DimensionVo oneDim = new DimensionVo();
                oneDim.setId(childrenItem.getName());
                oneDim.setTitle(childrenItem.getTitle());

                List<DimObj> testChildren = dimProvider.getChildren(childrenItem.getName());
                oneDim.setIsLeaf(testChildren.isEmpty());

                dimensionVoList.add(oneDim);
            }
        } catch (ABCIntfException e) {
            logger.error("逐级获取下级维度节点异常", e);
        }
        return dimensionVoList;
    }

    /**
     * 根据 后代维度编码 获取 溯源路径
     * @param dimName 后代维度编码
     * @return 溯源路径
     */
    public List<String> getDimensionTracePath(String dimName) {
        //溯源路径
        List<String> path = new ArrayList<>();
        try {
            DimBean bean = dimProvider.getBean(dimName);
            path.add(bean.getName());
            String pId = bean.getParent();
            while (!"-".equals(pId)) {
                DimFolder fatherFolder = dimProvider.getParent(pId);
                if (fatherFolder != null) {
                    path.add(pId);
                    pId = fatherFolder.getParent();
                } else {
                    pId = null;
                }
            }
            Collections.reverse(path);
        } catch (ABCIntfException e) {
            logger.error("根据后代维度编码获取溯源路径异常", e);
        }
        return path;
    }


    /**
     * 根据 模型ID、成本维度类型  获取 指定类型维度所有维度项
     *
     * @param modelId 模型ID
     * @param dimType 成本维度类型
     * @return 指定类型维度所有维度项
     */
    public List<DimensionVo> getDimensionMemberListByType(String modelId,int dimType) {
        List<DimBeanVo> dimList = abcModelService.queryModelDimByModelGuidAndType(modelId, dimType);

        //维度信息集合[前端展示]
        List<DimensionVo> dimensionVoList = new ArrayList<>();
        dimList.forEach(dimBeanVo -> {
            DimensionVo dimensionVo = new DimensionVo();
            dimensionVo.setKey(dimBeanVo.getName());
            dimensionVo.setTitle(dimBeanVo.getTitle());
            dimensionVoList.add(dimensionVo);
        });

        //获取当前模型 关联的 所有 指定类型 维度编码 并 构建查询参数集合
        ABCHomeVo abcHomeVo = abcModelService.queryPeriodAndDW(modelId);
        List<String> periodBeanList = abcHomeVo.getPeriodBeanList();
        String period = periodBeanList.get(periodBeanList.size() - 1);
        List<DimensionParameterVo> dimensionParameterVos = new ArrayList<>();
        for (DimBeanVo dimBeanVo : dimList) {
            String dimName = dimBeanVo.getName();
            DimensionParameterVo dimensionParameterVo = new DimensionParameterVo(modelId, period, dimName);
            dimensionParameterVos.add(dimensionParameterVo);
        }

        //获取 当前维度类型下  所有维度项信息[前端展示]
        List<DimensionMemberVo> dimensionMemberVoList = getDimensionMemberListByVos(dimensionParameterVos);

        //把扁平的维度项 按照 维度名称（dimName）进行分组
        //在 分组后形成的流 中 将 DimensionMemberVo 装配到  DimensionVo中
        dimensionMemberVoList.stream().collect(Collectors.groupingBy(DimensionMemberVo::getDimName)).forEach((key, value) ->
                dimensionVoList.stream()
                        .filter(preFilterDim -> preFilterDim.getKey().equals(key))
                        .forEach(filteredDim -> {
                            AtomicInteger totalCount = new AtomicInteger();
                            value.forEach(memberItem -> {
                                //维度项树形除根节点外下级节点个数
                                totalCount.addAndGet(memberItem.getSubDimItemCount());
                                //维度 包含维度项个数 应该包含维度项树形根节点个数
                                totalCount.addAndGet(1);
                            });
                            filteredDim.setMemberCount(totalCount.get());

                            filteredDim.setMemberVoList(value);
                        }));

        return dimensionVoList;
    }

    /**
     * 获取多个维度 下 所有维度项 信息
     *
     * @param dimensionParameterVos 多个维度请求参数集合 每一项的 属性： String modelId, String period, String dimName
     * @return 维度项信息集合
     */
    public List<DimensionMemberVo> getDimensionMemberListByVos(List<DimensionParameterVo> dimensionParameterVos) {
        List<DimensionMemberVo> dimensionMemberVos = new ArrayList<>();

        for (DimensionParameterVo dimensionParameterVo : dimensionParameterVos) {
            List<DimensionMemberVo> dimensionMemberList = getDimensionMemberListByVo(dimensionParameterVo);
            dimensionMemberVos.addAll(dimensionMemberList);
        }
        return dimensionMemberVos;
    }

    /**
     * 获取某一维度 下 所有维度项 信息
     *
     * @param parameterVo 获取维度项信息请求参数 属性： String modelId, String dimName
     * @return List<DimensionMemberVo>   维度项信息集合
     */
    public List<DimensionMemberVo> getDimensionMemberListByVo(DimensionParameterVo parameterVo) {
        List<Member> dimMemberList = getDimensionMemberListByDimName(parameterVo);
        //构建数据结构供前端使用
        //准备dimName,dimTitle数据
        String dimName = parameterVo.getDimName();
        String dimTitle = "";
        List<DimBeanVo> allDimBeanVoList = abcModelService.queryModelDimListByModelId(parameterVo.getModelId());
        for (DimBeanVo dimBeanVo : allDimBeanVoList) {
            if (dimBeanVo.getName().equals(dimName)) {
                dimTitle = dimBeanVo.getTitle();
            }
        }

        //①创建查询结构
        HashMap<String, DimensionMemberVo> memberVoHashMap = new HashMap<>();
        for (Member dimMemberItem : dimMemberList) {
            DimensionMemberVo dimensionMemberVo = new DimensionMemberVo();
            BeanUtils.copyProperties(dimMemberItem, dimensionMemberVo);

            dimensionMemberVo.setTitle(dimMemberItem.getName());
            dimensionMemberVo.setDimName(dimName);
            dimensionMemberVo.setDimTitle(dimTitle);
            memberVoHashMap.put(dimMemberItem.getCode(), dimensionMemberVo);
        }


        return processParentChildStructureData(dimMemberList, memberVoHashMap);
    }

    /**
     * 根据 包含父子结构信息的维度项集合  和  维度项查询结构  处理父子结构信息
     *
     * @param dimMemberList   包含父子结构信息的数据项集合
     * @param memberVoHashMap 数据项查询结构
     * @return 处理后维度项集合
     */
    public List<DimensionMemberVo> processParentChildStructureData(List<Member> dimMemberList, Map<String, DimensionMemberVo> memberVoHashMap) {
        //②构建父子层级
        List<String> rootCodeList = new ArrayList<>();    //记录根节点，后期取数使用
        dimMemberList.forEach(dim1MemberItem -> {
            //根节点（没有父级）
            if ("-".equals(dim1MemberItem.getParent()) || dim1MemberItem.getParent() == null) {
                rootCodeList.add(dim1MemberItem.getCode());
                //子节点（存在父级）
            } else {
                //遍历到的子节点 维成员VO
                DimensionMemberVo sonDimensionMemberVo = memberVoHashMap.get(dim1MemberItem.getCode());
                //该子节点的父级节点 维成员VO
                DimensionMemberVo dimensionMemberVo = memberVoHashMap.get(dim1MemberItem.getParent());

                //父级节点维成员VO 的 下级维成员集合
                List<DimensionMemberVo> subMemberResourceList = dimensionMemberVo.getSubMemberResourceList();
                List<DimensionMemberVo> children = dimensionMemberVo.getChildren();
                if (subMemberResourceList == null) {
                    List<DimensionMemberVo> dimensionMemberVos = new ArrayList<>();
                    dimensionMemberVos.add(sonDimensionMemberVo);
                    dimensionMemberVo.setSubMemberResourceList(dimensionMemberVos);
                    dimensionMemberVo.setChildren(dimensionMemberVos);
                } else {
                    subMemberResourceList.add(sonDimensionMemberVo);
                    dimensionMemberVo.setSubMemberResourceList(subMemberResourceList);
                    dimensionMemberVo.setChildren(children);
                }
            }
        });

        //③生成根节点集合
        List<DimensionMemberVo> dimensionMemberVoList = new ArrayList<>();
        for (String s : rootCodeList) {
            dimensionMemberVoList.add(memberVoHashMap.get(s));
        }

        //④计算前端所需参数
        for (DimensionMemberVo dimensionMemberVo : dimensionMemberVoList) {

            //添加ncell折叠、展开方法所需参数  -- 子节点个数
            recursiveCountSubMembers(dimensionMemberVo);

            //添加ncell折叠、展开方法所需参数  -- 节点层级
            recursiveCountLevels(dimensionMemberVo);
        }

        return dimensionMemberVoList;
    }

    //<editor-fold desc="递归计算下级节点个数">

    /**
     * 递归计算下级节点个数
     *
     * @param rootMemberVo 根维成员节点
     */
    public void recursiveCountSubMembers(DimensionMemberVo rootMemberVo) {
        if (rootMemberVo.getSubMemberResourceList() == null) {
            rootMemberVo.setSubDimItemCount(0);
            return;
        }

        int subMemberCount = getSubMemberCount(rootMemberVo);
        rootMemberVo.setSubDimItemCount(subMemberCount);

        //递归计算所有节点的下级节点个数
        List<DimensionMemberVo> children = rootMemberVo.getSubMemberResourceList();
        for (DimensionMemberVo child : children) {
            recursiveCountSubMembers(child);
        }
    }

    private int getSubMemberCount(DimensionMemberVo rootMemberVo) {
        //从0开始计算根节点（会动态变化）的下级节点个数。
        // 每个根节点对应一个新的count，除第一个新建count外，其余count存在的意义只为第一个count服务（加和）
        int count = 0;
        List<DimensionMemberVo> childrenList = rootMemberVo.getSubMemberResourceList();
        //程序首次运行时，此处条件不满足（调用getSubMemberCount之前已经进行了处理）。执行递归时将会满足
        if (childrenList == null) {
            //该节点不存在子节点了，返回0。此时是递归  下钻到底 后 上卷 的判断条件
            return 0;
        }
        for (DimensionMemberVo childMemberVo : childrenList) {
            //节点x的下级节点个数 + 节点x自身节点个数（1）
            count += getSubMemberCount(childMemberVo) + 1;
        }
        //当一个子节点列表中所有节点都进行了递归计算，将计算结果返回上级，进行加和操作
        return count;
    }
    //</editor-fold>

    //<editor-fold desc="递归计算节点层级">

    /**
     * 递归计算节点层级
     *
     * @param rootMemberVo 根维成员节点
     */
    public void recursiveCountLevels(DimensionMemberVo rootMemberVo) {
        int level = 0;
        recursiveCountLevels(rootMemberVo, level);
    }

    private void recursiveCountLevels(DimensionMemberVo memberVo, int level) {
        memberVo.setTableLevel(level);

        //动因项详情 的 左树右表中，树形数据默认展开前两级
        if (level == 0 || level == 1)
            memberVo.setExpand(true);
        if (level > 1)
            memberVo.setExpand(false);

        List<DimensionMemberVo> subList = memberVo.getSubMemberResourceList();
        if (subList == null)
            //递归下钻到底 后 上卷的判断条件
            return;
        for (DimensionMemberVo submemberVo : subList) {
            //level值 在递归下钻时 加一。当上卷后，level值是下钻前的值
            recursiveCountLevels(submemberVo, level + 1);
        }
    }
    //</editor-fold>


    /**
     * 根据 维度名称、模型ID  获取 维度项集合 并进行 模糊匹配
     *
     * @param parameterVo 维度名称、模型ID、查询内容
     * @return 模糊匹配后维度项集合
     */
    public List<Member> fuzzyMatchDimMembers(DimensionParameterVo parameterVo) {
        List<Member> dimMemberList = getDimensionMemberListByDimName(parameterVo);

        //进行模糊匹配
        List<Member> filtedDimMemberList = new ArrayList<>();
        dimMemberList.stream()
                .filter(preFilteItem -> preFilteItem.getName().contains(parameterVo.getSearchKey().trim())
                                        ||
                                        preFilteItem.getCode().contains(parameterVo.getSearchKey().trim().toLowerCase())
                                        ||
                                        preFilteItem.getCode().contains(parameterVo.getSearchKey().trim().toUpperCase())
                )
                .forEach(filtedDimMemberList::add);

        return filtedDimMemberList;
    }

    /**
     * 根据 维度项信息请求参数  获取维度项
     * @param parameterVo 请求参数   维度代码：一定存在   模型ID：可能存在
     * @return 下级维度项
     */
    private List<Member> getDimensionMemberListByDimName(DimensionParameterVo parameterVo) {
        List<Member> dimMemberList = new ArrayList<>();
        String period = "";
        if (StringUtils.isNotEmpty(parameterVo.getModelId())) {
            ABCHomeVo abcHomeVo = abcModelService.queryPeriodAndDW(parameterVo.getModelId());
            List<String> periodBeanList = abcHomeVo.getPeriodBeanList();
            period = periodBeanList.get(periodBeanList.size() - 1);
        }

        try {
            //获取某一维度下的所有维成员
            //当 modelId 为空时，时期参数为空字符串
            dimMemberList = memberProvider.get(parameterVo.getDimName(), period);

        } catch (ABCIntfException e) {
            logger.error("获取" + parameterVo.getDimName() + "维度下维度项信息异常", e);
        }
        return dimMemberList;
    }

    /**
     * 根据 维度编码 和 维度项编码集合 获取 维度项列表
     * @param dimName 维度编码
     * @param memberCodes 维度项编码集合
     * @return 维度项列表
     */
    public List<Member> getDimensionMemberListByNameAndCodes(String dimName,List<String> memberCodes) {
        List<Member> result = new ArrayList<>();
        try {
            result =  memberProvider.get(dimName, memberCodes, "");
        } catch (ABCIntfException e) {
            logger.error("根据维度编码和维度项编码集合获取维度项列表异常", e);
        }
        return result;
    }

}
